﻿using AiMoBridge.Interface;
using AiMoWindows.CodeScripts;
using AiMoWindows.Entrance;
using AiMoWindows.MainActions;
using AiMoWindows.MainActions.Mode;
using AiMoWindows.MainActions.ScriptActions;
using AiMoWindows.Windows.Scripts.ViewModels;
using AiMoWindows.Windows.Themes;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Threading;
using RoslynPad.Roslyn;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace AiMoWindows.Windows.Scripts
{
    /// <summary>
    /// Interaction logic for ScriptWindow.xaml
    /// </summary>
    partial class ScriptWindow : Window, IAiMoWindow
    {
        readonly ScriptViewModel m_ViewModel;
        readonly ScriptManager scriptManager;
        readonly ActionsController actions;
        public ScriptWindow(ScriptManager scriptManager)
        {
            this.scriptManager = scriptManager;
            InitializeComponent();
            actions = new ActionsController(Editor, scriptManager, this);
            m_ViewModel = new ScriptViewModel(this, actions, RenameDialog);
            DataContext = m_ViewModel;
            SetCompileMode(scriptManager.CompileMode);
            // 一开始默认都是 AiMoScript，除非后期修改，或者Read了一个已有的
            scriptManager.CompileModeChanged += SetCompileMode;
            RegisterEvent();
        }
        public void SetCompileMode(ScriptComplileMode scriptMode)
        {
            ExecuterMode executerMode = AiMoProgram.Instance.AiMoExecuterMode;

            if (scriptMode == ScriptComplileMode.Unset)
            {
                MessageBox.Show("Debug Error: SetCompileMode is Unset");
            }

            int caseNumber = (int)scriptMode + (int)executerMode;

            switch (caseNumber)
            {
                // Rhino 模式下 匹配了 Rhino 代码
                case (int)ExecuterMode.Rhino + (int)ScriptComplileMode.RhinoScript:
                    MainWindow.Background = new SolidColorBrush(Color.FromRgb(40, 40, 40));
                    m_ViewModel.ScriptUI.TitlePrefix = "AiMo";
                    break;
                // GH 模式下匹配了 GH 代码
                case (int)ExecuterMode.Grasshopper + (int)ScriptComplileMode.GrasshopperScript:
                    MainWindow.Background = new SolidColorBrush(Color.FromRgb(42, 66, 61));
                    m_ViewModel.ScriptUI.TitlePrefix = "AiMo GH";
                    m_ViewModel.ScriptUI.PingCommand.Execute(this);
                    break;

                // 可以输出Dll模式
                case (int)ExecuterMode.Grasshopper + (int)ScriptComplileMode.ExternalAssemblies:
                case (int)ExecuterMode.Rhino + (int)ScriptComplileMode.ExternalAssemblies:
                case (int)ExecuterMode.None + (int)ScriptComplileMode.ExternalAssemblies:
                    // 没有Rhino连接
                    // 只能编译
                    MainWindow.Background = new SolidColorBrush(Color.FromRgb(49, 56, 72));
                    m_ViewModel.ScriptUI.TitlePrefix = "Compiler";
                    m_ViewModel.ScriptUI.Readonly = false;
                    m_ViewModel.ScriptUI.CompileOnly = true;
                    break;
                // 仅编译模式
                default:
                case (int)ExecuterMode.Grasshopper + (int)ScriptComplileMode.RhinoScript:
                case (int)ExecuterMode.Rhino + (int)ScriptComplileMode.GrasshopperScript:
                case (int)ExecuterMode.None + (int)ScriptComplileMode.RhinoScript:
                case (int)ExecuterMode.None + (int)ScriptComplileMode.GrasshopperScript:
                    MainWindow.Background = new SolidColorBrush(Color.FromRgb(66, 64, 47));
                    m_ViewModel.ScriptUI.TitlePrefix = "Readonly";
                    m_ViewModel.ScriptUI.Readonly = true;
                    m_ViewModel.ScriptUI.CompileOnly = true;
                    break;
            }

        }
        public async void InitializeEditor()
        {
            Editor.Focus();
            Editor.TextArea.Options.IndentationSize = 4;
            Editor.TextArea.Options.ConvertTabsToSpaces = true;

            string workingDirectory = Directory.GetCurrentDirectory();
            RoslynHostReferences hostReferences = RoslynHostReferences.NamespaceDefault.With(references: scriptManager.AllReferences);
            // https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/
            RoslynHost scriptsHost = new CustomRoslynHost(hostReferences, ["CS1701", "CS1702", "CS7011", "CS8097", "IDE0090", "IDE0028", "IDE0290", "IDE0300", "IDE0301", "IDE0305", "CA1822"]);
            Editor.RefreshHighlighting();

            DarkHighlightColors darkHighlightColors = new DarkHighlightColors();

            var documentId = await Editor.InitializeAsync(scriptsHost, darkHighlightColors,
                workingDirectory, scriptManager.CurrentCodeText(), SourceCodeKind.Regular).ConfigureAwait(true);
            //Document document = scriptsHost.GetDocument(documentId) ?? throw new Exception("Cannot initialized due to invalid document");
            //Microsoft.CodeAnalysis.Options.DocumentOptionSet optionSet = await document.GetOptionsAsync().ConfigureAwait(false);
            //var formatOption = optionSet.WithChangedOption(new Microsoft.CodeAnalysis.Options.OptionKey(Microsoft.CodeAnalysis.Formatting.FormattingOptions.IndentationSize, LanguageNames.CSharp), 4);

            actions.NameMatchAction.SetTextMatchHighlightRenderer(new TextMatchHighlightRenderer(Editor.TextArea.TextView, darkHighlightColors));
            await actions.Initialize(scriptsHost, documentId);

            m_ViewModel.CurrentScriptPath = scriptManager.SavePath;
            scriptManager.OnSavePathChanged += m_ViewModel.OnSavePathChanged;

            m_ViewModel.ScriptUI.TextFontSize = ExternalSettings.EditorFontSize;

            //Editor.TextArea.TextView
        }
        // 关于 代码补全，入口在 Editor.InitializeAsync 中，
        // 绘制菜单：
        // 通过ContextActionsRenderer的 CreateContextMenu() => LoadActionsWithCancellationAsync() => LoadActionsAsync() => provider.GetActions()获取所有的Action
        // provider.GetActions() 是 class RoslynContextActionProvider 中的 public async Task<IEnumerable<object>> GetActions
        // 通过 _roslynHost.GetService<ICodeRefactoringService>() 获取 CodeRefactorings : class RoslynPad.Roslyn.CodeRefactorings.CodeRefactoringService
        // 通过 _roslynHost.GetService<ICodeFixService>() 获取 codeFixes : class RoslynPad.Roslyn.CodeFixes.CodeFixService
        DateTime _EPressedTime = DateTime.MinValue;
        /// <summary>
        /// 快捷键
        /// </summary>
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);
            if (!e.Handled)
            {
                if (e.KeyboardDevice.Modifiers == ModifierKeys.Control)
                {
                    switch (e.Key)
                    {
                        case Key.E:
                            _EPressedTime = DateTime.Now;
                            e.Handled = true;
                            break;
                        case Key.U:
                            if ((DateTime.Now - _EPressedTime).TotalMilliseconds < 800)
                            {
                                _EPressedTime = DateTime.MinValue;
                                e.Handled = true;
                                m_ViewModel.UncommentScriptCommand.Execute(null);
                            }
                            break;
                        case Key.C:
                            if ((DateTime.Now - _EPressedTime).TotalMilliseconds < 800)
                            {
                                _EPressedTime = DateTime.MinValue;
                                e.Handled = true;
                                m_ViewModel.CommmentScriptCommand.Execute(null);
                            }
                            break;
                        case Key.W:
                            if ((DateTime.Now - _EPressedTime).TotalMilliseconds < 800)
                            {
                                _EPressedTime = DateTime.MinValue;
                                e.Handled = true;
                                Editor.WordWrap = !Editor.WordWrap;
                            }
                            break;
                    }
                }
                //else if(e.KeyboardDevice.Modifiers == ModifierKeys.None)
                //{
                //    previousKey = e.Key;
                //}
            }
        }

        private void OnUnhandledDispatcherException(object? sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs args)
        {
            string text = Localization.Local.RunningInChinese ?
                $"异常：{Environment.NewLine}  AiMo 代码编辑器遇到了一个未处理的错误，请联系开发者：{Environment.NewLine}  wucl12@foxmail.com" :
                $"Error:{Environment.NewLine}  AiMo Editor meet an Unhandled Exception, Please Contact Author: {Environment.NewLine}  wucl12@foxmail.com";
            text += Environment.NewLine + args.Exception.FormatException();
            PrintDebug(text);
            args.Handled = true;
        }
        #region DragDrop
        void OnDragOver(object obj, DragEventArgs e)
        {
            if (e.Data.GetData(DataFormats.FileDrop, false) is string[] files
                && files.Length == 1
                && CodeScripts.IO.CodeIO.IsValidAiMoScriptFilePath(files[0]))
                e.Effects = DragDropEffects.Link;
            else
                e.Effects = DragDropEffects.None;
        }
        void OnDrop(object obj, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                foreach (string file in files)
                {
                    // 在这里添加处理每个文件的代码
                    actions.OpenSaveAction.OpenScriptWithPath(file);
                }
            }
        }
        #endregion
        void RegisterEvent()
        {
            Application.Current.DispatcherUnhandledException += OnUnhandledDispatcherException;
            Editor.PreviewMouseWheel += m_ViewModel.ScriptUI.MouseWheelChangeSize;
            OutputTextBox.PreviewMouseWheel += m_ViewModel.ScriptUI.MouseWheelChangeSize;
            Editor.TextArea.Caret.PositionChanged += CursorPositionChanged;
            Editor.TextArea.SelectionChanged += CursorPositionChanged;
            Editor.TextArea.Document.Changed += m_ViewModel.Document_Changed;
            Editor.TextArea.Document.UpdateFinished += m_ViewModel.Update_Finished;
            Editor.PreviewMouseDoubleClick += actions.NameMatchAction.OnDoubleClickAndMarkText;
            Editor.TextArea.TextEntering += actions.CompletionAction.OnTextEntering;
        }


        void UnregisterEvent()
        {
            Application.Current.DispatcherUnhandledException -= OnUnhandledDispatcherException;
            Editor.PreviewMouseWheel -= m_ViewModel.ScriptUI.MouseWheelChangeSize;
            OutputTextBox.PreviewMouseWheel -= m_ViewModel.ScriptUI.MouseWheelChangeSize;
            Editor.TextArea.Caret.PositionChanged -= CursorPositionChanged;
            Editor.TextArea.SelectionChanged -= CursorPositionChanged;
            Editor.TextArea.Document.Changed -= m_ViewModel.Document_Changed;
            Editor.TextArea.Document.UpdateFinished -= m_ViewModel.Update_Finished;
            Editor.PreviewMouseDoubleClick -= actions.NameMatchAction.OnDoubleClickAndMarkText;
            Editor.TextArea.TextEntering -= actions.CompletionAction.OnTextEntering;
        }
        private void CursorPositionChanged(object? sender, EventArgs eventArgs)
        {
            actions.NameMatchAction.ClearMarkText();
            int count = Editor.SelectionLength;
            m_ViewModel.CursorLocationInfo = $"Ln {Editor.TextArea.Caret.Line}, Col {Editor.TextArea.Caret.Column}" + ((count > 0) ? $", Sel {count}" : "");
        }


        #region IAimoWindow
        bool _ClosedActived = false;
        bool IAiMoWindow.CloseActived => _ClosedActived;
        void IAiMoWindow.UpdateState()
        {
            m_ViewModel.ScriptUI.UpdateAllUIWindows();
            m_ViewModel.ScriptUI.UpdateAttention();
        }

        protected override void OnClosing(CancelEventArgs e)
        {

            SaveFontSize();
            Task<bool> task = actions.ClearAction.CheckNotSaveOrCancel();
            task.Wait();
            if (task.Result)
            {
                e.Cancel = true;
                return;
            }
            base.OnClosing(e);
        }
        void SaveFontSize()
        {
            ExternalSettings.EditorFontSize = m_ViewModel.ScriptUI.TextFontSize;
            Task.Run(ExternalSettings.Save);
        }
        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            _ClosedActived = true;
            UnregisterEvent();
        }
        void IDebugPrinter.PrintDebug(string text)
        {
            PrintDebug(text);
        }
        public void ClearPrintText()
        {
            OutputTextBox.Dispatcher.InvokeAsync(() =>
            {
                OutputTextBox.Clear();
            });
        }
        public void PrintDebug(string message)
        {
            if (!_ClosedActived && !string.IsNullOrEmpty(message))
            {
                OutputTextBox.Dispatcher.InvokeAsync(() =>
                {
                    OutputTextBox.AppendText($"[{DateTime.Now:HH:mm:ss}] " + message + Environment.NewLine);
                    if (OutputTextBox.LineCount > 300)
                    {
                        int index = OutputTextBox.GetCharacterIndexFromLineIndex(OutputTextBox.LineCount - 300);
                        OutputTextBox.Text = OutputTextBox.Text[index..];
                    }
                    OutputTextBox.ScrollToEnd();
                });
            }
        }
        #endregion
    }
}